﻿using System.Collections.Concurrent;
using System.CommandLine;
using System.Reflection;

namespace zijian666.CommandLine;

/// <summary>
/// 命令行构建器
/// </summary>
internal class CommandLineBuilder
{
    /// <summary>
    /// 选项构建器缓存
    /// </summary>
    static readonly ConcurrentDictionary<Type, IOptionBuilder> OPTION_BUILDERS = [];
    /// <summary>
    /// 根据命令类型构建命令
    /// </summary>
    /// <typeparam name="TCommand"> 命令类型，必须包含 Execute() 方法 </typeparam>
    /// <param name="command"> 可选的命令对象, 如果为 null 则创建一个新的命令对象, 否则在此命令对象上添加选项 </param>
    /// <returns> 命令对象 </returns>
    /// <exception cref="InvalidOperationException"> 当 TCommand 类型没有实现 Execute() 方法时抛出 </exception>
    public static Command BuildCommand<TCommand>(Command? command)
    {
        var type = typeof(TCommand);
        var execute = type.GetMethod("Execute", []);
        if (execute == null)
        {
            throw new InvalidOperationException($"类型 {type.FullName} 没有实现 Execute() 方法  ");
        }
        var binder = new ParseResultBinder();
        var commandAttr = type.GetCustomAttribute<CommandLineActionAttribute>() ?? new();
        commandAttr.Resolve(type);
        if (command is null)
        {
            command = new Command(commandAttr.Name, commandAttr.Description);
            if (commandAttr.Alias.Length > 0)
            {
                foreach (var alias in commandAttr.Alias)
                {
                    command.Aliases.Add(alias);
                }
            }
        }
        foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.Instance))
        {
            if (member is not (PropertyInfo or FieldInfo))
            {
                continue;
            }
            var optionAttr = member.GetCustomAttribute<OptionAttribute>() ?? new();
            if (optionAttr.Ignore)
            {
                continue;
            }
            optionAttr.Resolve(member);

            var optionBuilder = OPTION_BUILDERS.GetOrAdd(optionAttr.ValueType, IOptionBuilder.Create);
            var option = optionBuilder.Build(optionAttr);

            command.Add(option);
            binder.BindActions.Add((instance, parseResult) =>
            {
                var value = optionBuilder.GetValue(option, parseResult);

                switch (member)
                {
                    case PropertyInfo property:
                        property.SetValue(instance, value);
                        break;
                    case FieldInfo field:
                        field.SetValue(instance, value);
                        break;
                }
            });
        }

        command.SetAction(args =>
        {
            var instance = Activator.CreateInstance(type)!;
            binder.Bind(instance, args);
            var result = execute.Invoke(instance, null);
            return result switch
            {
                Task<int> taskInt => taskInt,
                int intResult => Task.FromResult(intResult),
                Task task => task.ContinueWith(t => 0),
                _ => Task.FromResult(0)
            };
        });

        return command;
    }

    /// <summary>
    /// 解析结果绑定器
    /// </summary>
    class ParseResultBinder
    {
        /// <summary>
        /// 绑定动作集合
        /// </summary>
        public ICollection<Action<object, ParseResult>> BindActions { get; } = [];
        /// <summary>
        /// 绑定解析结果到实例
        /// </summary>
        /// <param name="instance"> 实例对象 </param>
        /// <param name="parseResult"> 解析结果 </param>
        public void Bind(object instance, ParseResult parseResult)
        {
            foreach (var action in BindActions)
            {
                action(instance, parseResult);
            }
        }
    }
}
